/*
* full-citys-api.js 省市区数据操作
* author:devin87@qq.com  
* update:2023/08/21 09:51
*/
(function (undefined) {
    var dataAll = { countries: [] },
        map_country_by_id = {},
        map_data_by_code = {};

    /**
     * 数据类型
     * @enum {number}
     */
    var Types = {
        country: 1,
        prov: 2,
        city: 3,
        area: 4
    };

    var map_code_by_name = {
        '河北区': 'CN101031600',
        '河南县': 'CN101150304',
        '海南区': 'CN101080303',
        '海南州': 'CN101150402',
        '吉林市': 'CN101060201',
        '和田市': 'CN101131308',
        '和田县': 'CN101131309'
    };

    /**
     * @typedef IData
     * @property {number} id ID
     * @property {string} code 代码
     * @property {string} name 名称
     * @property {number} type 类型
     * @property {string} _name 格式化后的名称
     */
    /**
     * 将数组数据转为对象
     * @param {Array} data 原始数据
     * @param {number} type 类型，见 Types
     * @property {function} formatName 名称格式化函数
     * @returns {IData}
     */
    function formatRawData(data, type, formatName) {
        var result = { type: type, id: data[0], name: data[1], code: data[2] };
        if (formatName && result.name) result._name = formatName(result.name);

        return result;
    }

    /**
     * 获取子项列表
     * @returns {Array.<Array>}
     */
    function getRawSubs(key) {
        return dataAllCitys[key] || [];
    }

    /**
     * 格式化省份名称 eg:湖北省 => 湖北
     * @param {string} prov
     * @returns {string}
     */
    function formatProv(prov) {
        if (prov.length <= 2) return prov;
        return prov.replace(/[省市]$|国家及地区$/g, '');
    }

    /**
     * 格式化城市名称 eg:武汉市 => 武汉
     * @param {string} city
     * @returns {string}
     */
    function formatCity(city) {
        var name = city.length <= 2 ? city : city.replace(/国家及地区$|特别行政区$|地区$|[州市盟县]$/g, '');
        return name.replace('襄樊', '襄阳').replace('思茅', '普洱').replace('克州', '克孜勒州');
    }

    /**
     * 格式化地区名称 eg:洪山区 => 洪山
     * @param {string} area 
     * @returns {string}
     */
    function formatArea(area) {
        if (area.length <= 2) return area;
        return area.replace(/[市县区]$/g, '');
    }

    /**
     * 初始化数据
     */
    function init() {
        dataAll = { countries: [] };

        (dataAllCitys.countries || [[0, '中国']]).forEach(function (data) {
            var data_country = formatRawData(data, Types.country),
                key_provs = data_country.id,
                list_prov = [],
                map_prov_by_id = {},
                map_prov_by_name = {},
                map_country_city_by_name = {},
                map_country_area_by_name = {};

            data_country.ctid = data_country.id;

            //provs
            getRawSubs(key_provs).forEach(function (data2) {
                var data_prov = formatRawData(data2, Types.prov, formatProv),
                    key_citys = data_country.id + '_' + data_prov.id,
                    list_city = [],
                    map_city_by_id = {},
                    map_city_by_name = {},
                    map_prov_area_by_name = {};

                data_prov.ctid = data_country.id;
                data_prov.pid = data_prov.id;

                //citys
                getRawSubs(key_citys).forEach(function (data3) {
                    var data_city = formatRawData(data3, Types.city, formatCity),
                        key_areas = data_country.id + '_' + data_prov.id + '_' + data_city.id,
                        list_area = [],
                        map_area_by_id = {},
                        map_area_by_name = {};

                    data_city.ctid = data_prov.ctid;
                    data_city.pid = data_prov.id;
                    data_city.cid = data_city.id;

                    //areas
                    getRawSubs(key_areas).forEach(function (data4) {
                        var data_area = formatRawData(data4, Types.area, formatArea);

                        data_area.ctid = data_city.ctid;
                        data_area.pid = data_city.pid;
                        data_area.cid = data_city.id;
                        data_area.aid = data_area.id;

                        list_area.push(data_area);
                        map_country_area_by_name[data_area._name] = map_country_area_by_name[data_area.name] = map_prov_area_by_name[data_area._name] = map_prov_area_by_name[data_area.name] = map_area_by_name[data_area._name] = map_area_by_name[data_area.name] = map_area_by_id[data_area.id] = map_data_by_code[data_area.code] = data_area;
                    });

                    list_city.push(data_city);
                    map_city_by_id[data_city.id] = map_data_by_code[data_city.code] = data_city;

                    var city_names = (data_city.name || '').split('-');
                    if (city_names.length > 0) {
                        city_names.forEach(function (name) {
                            name = name.trim();
                            if (!name) return;

                            var _name = formatCity(name);
                            map_country_city_by_name[_name] = map_city_by_name[_name] = map_country_city_by_name[name] = map_city_by_name[name] = data_city;
                        });
                    }

                    data_city.mapAreaId = map_area_by_id;
                    data_city.mapAreaName = map_area_by_name;

                    dataAll[key_areas] = list_area;
                });

                list_prov.push(data_prov);
                map_prov_by_id[data_prov.id] = data_prov;

                var prov_names = (data_prov.name || '').split('-');
                if (prov_names.length > 0) {
                    prov_names.forEach(function (name) {
                        name = name.trim();
                        if (!name) return;

                        var _name = formatCity(name);
                        map_prov_by_name[_name] = map_prov_by_name[name] = data_prov;
                    });
                }

                data_prov.mapCityId = map_city_by_id;
                data_prov.mapCityName = map_city_by_name;
                data_prov.mapAreaName = map_prov_area_by_name;

                dataAll[key_citys] = list_city;
            });

            data_country.mapProvId = map_prov_by_id;
            data_country.mapProvName = map_prov_by_name;
            data_country.mapCityName = map_country_city_by_name;
            data_country.mapAreaName = map_country_area_by_name;

            map_country_by_id[data_country.id] = data_country;

            dataAll[key_provs] = list_prov;
            dataAll.countries.push(data_country);
        });
    }

    init();

    /**
     * @typedef IArea
     * @property {number} id 地区ID
     * @property {string} code 地区代码
     * @property {string} name 城市名称
     * @property {string} _name 格式化后的名称
     * @property {number} ctid 国家ID
     * @property {number} pid 省份ID
     * @property {number} cid 城市ID
     */
    /**
     * @typedef ICity
     * @property {number} id 城市ID
     * @property {string} code 城市代码
     * @property {string} name 城市名称
     * @property {string} _name 格式化后的名称
     * @property {number} ctid 国家ID
     * @property {number} pid 省份ID
     * @property {object.<number,IArea>} mapAreaId 区域ID索引
     * @property {object.<string,IArea>} mapAreaName 区域名称索引
     */
    /**
     * @typedef IProv
     * @property {number} id 省份ID
     * @property {string} name 省份名称
     * @property {string} _name 格式化后的名称
     * @property {number} ctid 国家ID
     * @property {object.<number,ICity>} mapCityId 城市ID索引
     * @property {object.<string,ICity>} mapCityName 城市名称索引
     * @property {object.<string,IArea>} mapAreaName 地区名称索引
     */
    /**
     * @typedef ICountry
     * @property {number} id 国家ID
     * @property {string} name 国家名称
     * @property {number} type 类型，见 Types
     * @property {object.<number,IProv>} mapProvId 省份ID索引
     * @property {object.<string,IProv>} mapProvName 省份名称索引
     * @property {object.<string,ICity>} mapCityName 城市名称索引
     * @property {object.<string,IArea>} mapAreaName 地区名称索引
     */
    /**
     * 获取国家列表 eg: [{id:0,name:'中国'}]
     * @returns {Array.<ICountry>}
     */
    function getCountries() {
        return dataAll.countries;
    }

    /**
     * 获取子项列表
     * @param {string} key eg: 0|0_0|0_0_0
     * @returns {Array.<IData>}
     */
    function getSubs(key) {
        return dataAll[key] || [];
    }
    /**
     * 根据国家id获取省份列表 eg: [{id:0,name:'北京',ctid:0}]
     * @param {string} ctid 国家id,默认为0
     * @returns {Array.<IProv>}
     */
    function getProvs(ctid) {
        return getSubs(ctid || '0');
    }
    /**
     * 根据国家id、省份id获取城市列表 eg: [{id:0,name:'海淀',ctid:0,pid:0}]
     * @param {string} pid 省份id,不能为空
     * @param {string} ctid 国家id,默认为0
     * @returns {Array.<ICity>}
     */
    function getCitys(pid, ctid) {
        return getSubs((ctid || '0') + '_' + pid);
    }

    /**
     * 根据国家id、省份id、城市id获取城区列表 eg: [{id:0,name:'蔡甸',ctid:0,pid:19,cid:0}]
     * @param {string} pid 省份id,不能为空
     * @param {string} cid 城市id,不能为空
     * @param {string} ctid 国家id,默认为0
     * @returns {Array.<IArea>}
     */
    function getAreas(pid, cid, ctid) {
        return getSubs((ctid || '0') + '_' + pid + '_' + cid);
    }

    /**
     * 获取国家对象 eg: {id:0,name:'中国'}
     * @param {string} ctid 国家id,默认为0
     * @returns {ICountry}
     */
    function getCountry(ctid) {
        return map_country_by_id[ctid || '0'];
    }

    /**
     * 获取省份对象 eg: {id:0,name:'北京',ctid:0}
     * @param {string} pid 省份id,不能为空
     * @param {string} ctid 国家id,默认为0
     * @returns {IProv}
     */
    function getProv(pid, ctid) {
        var data_country = getCountry(ctid);
        return data_country ? data_country.mapProvId[pid] : undefined;
    }

    /**
     * 获取城市省份对象 eg: {id:0,name:'北京',ctid:0}
     * @param {string} pid 省份id,不能为空
     * @param {string} cid 城市id,不能为空
     * @param {string} ctid 国家id,默认为0
     * @returns {ICity}
     */
    function getCity(pid, cid, ctid) {
        var data_prov = getProv(pid, ctid);
        return data_prov ? data_prov.mapCityId[cid] : undefined;
    }

    /**
     * 获取城区对象 eg: {id:0,name:'蔡甸',ctid:0,pid:19,cid:0}
     * @param {string} pid 省份id,不能为空
     * @param {string} cid 城市id,不能为空
     * @param {string} aid 城区id,不能为空
     * @param {string} ctid 国家id,默认为0
     * @returns {IArea}
     */
    function getArea(pid, cid, aid, ctid) {
        var data_city = getCity(pid, cid, ctid);
        return data_city ? data_city.mapAreaId[aid] : undefined;
    }

    /**
     * 获取对象 eg: {id:0,name:'蔡甸',ctid:0,pid:19,cid:0}
     * @param {string} pid 省份id,为空则获取国家对象
     * @param {string} cid 城市id,为空则获取省份对象
     * @param {string} aid 城区id,为空则获取城市对象
     * @param {string} ctid 国家id,默认为0
     */
    function getAuto(pid, cid, aid, ctid) {
        if (aid != undefined) return getArea(pid, cid, aid, ctid) || getCity(pid, cid, ctid);
        if (cid != undefined) return getCity(pid, cid, ctid);
        if (pid != undefined) return getProv(pid, cid, ctid);
        return getCountry(ctid);
    }

    /**
     * @typedef FormatData
     * @property {number} type 类型(1:国家|2:省份|3:城市|4:地区)
     * @property {string} code 代码
     * @property {number} ctid 国家ID
     * @property {string} country 国家名称
     * @property {number} pid 省份ID
     * @property {string} prov 省份名称
     * @property {number} cid 城市ID
     * @property {string} city 城市名称
     * @property {number} aid 地区ID
     * @property {string} area 地区名称
     */
    /**
     * 格式化输出 eg: {type,code,ctid,pid,cid,aid,country,prov,city,area}
     * @param {object} data 省份、城市或地区对象
     * @returns {FormatData}
     */
    function formatData(data) {
        if (!data) return;

        var result = {};

        if (data.type) result.type = data.type;
        if (data.code) result.code = data.code;

        if (data.ctid != undefined) result.ctid = data.ctid;
        if (data.pid != undefined) result.pid = data.pid;
        if (data.cid != undefined) result.cid = data.cid;
        if (data.aid != undefined) result.aid = data.aid;

        result.country = (getCountry(result.ctid) || {}).name;
        if (result.pid != undefined) result.prov = (getProv(result.pid, result.ctid) || {}).name;
        if (result.cid != undefined) result.city = (getCity(result.pid, result.cid, result.ctid) || {}).name;
        if (result.aid != undefined) result.area = (getArea(result.pid, result.cid, result.aid, result.ctid) || {}).name;

        return result;
    }

    /**
     * 根据代码获取城市或地区对象
     * @param {string} code 
     * @returns {IData}
     */
    function getDataByCode(code) {
        return map_data_by_code[map_code_by_name[code] || code];
    }

    /**
     * 根据城市、地区代码或名称获取对象
     * @param {string} name 城市、地区代码或名称 eg: CN101010100|武汉
     * @param {boolean} is_format 是否格式化结果, 默认为true {pid,cid,id,name} => {type,code,ctid,pid,cid,aid,country,prov,city,area}
     * @param {string} ctid 国家id,默认为0
     * @returns {FormatData}
     */
    function getCityOrArea(code, is_format, ctid) {
        if (!code) return;

        var data = getDataByCode(code);
        if (!data) {
            var data_country = getCountry(ctid);
            if (data_country) data = data_country.mapCityName[code] || data_country.mapAreaName[code];
        }

        return data && is_format !== false ? formatData(data) : data;
    }

    /**
     * 根据城市、地区名称获取代码
     * @param {string} name 城市、地区名称 eg: 北京 => CN101010100
     * @param {string} ctid 国家id,默认为0
     * @returns {string}
     */
    function getCode(name, ctid) {
        var data = getCityOrArea(name, false, ctid);
        return data ? data.code : undefined;
    }

    /**
     * 在列表中查找指定名称对象
     * @param {Array.<IData>} list 对象列表 eg: [{id,code,name}]
     * @param {string} name 要查找的名称
     * @returns {IData}
     */
    function findInList(list, name) {
        for (var i = 0, len = list.length; i < len; i++) {
            var data = list[i];
            if (name.indexOf(data._name || data.name) != -1) return data;
        }
    }

    /**
     * 查找省份对象
     * @param {string} prov 省份名称 eg: 湖北|湖北武汉
     * @param {boolean} is_full_match 是否匹配完整名称，为false时将在所有省份列表中查找，默认为false
     * @param {string} ctid 国家id,默认为0
     * @returns {IProv}
     */
    function findProv(prov, is_full_match, ctid) {
        if (!prov) return;

        var _prov = formatProv(prov);

        var data_country = getCountry(ctid), data_prov;
        if (data_country) {
            data_prov = data_country.mapProvName[prov] || data_country.mapProvName[_prov];
            if (data_prov) return data_prov;
        }

        if (is_full_match) return;

        return findInList(getProvs(ctid), _prov);
    }

    /**
     * 查找城市对象
     * @param {string} city 城市名称或代码
     * @param {IProv|Array.<ICity>} data_prov 省份对象|城市列表,为空则查找所有城市
     * @param {boolean} is_full_match 是否匹配完整名称，为false时将在所有城市列表中查找，默认为false
     * @param {string} ctid 国家id,默认为0
     * @returns {ICity}
     */
    function findCity(city, data_prov, is_full_match, ctid) {
        if (!city) return;

        var data_city = getDataByCode(city);
        if (data_city) return data_city;

        var _city = formatCity(city);

        if (data_prov) {
            if (data_prov.mapCityName) {
                data_city = data_prov.mapCityName[city] || data_prov.mapCityName[_city];
                if (data_city) return data_city;
            }

            if (data_prov.id) return findInList(getCitys(data_prov.id, data_prov.ctid), _city);

            if (Array.isArray(data_prov)) return findInList(data_prov, _city);
        }

        var data_country = getCountry(ctid);
        if (data_country) {
            data_city = data_country.mapCityName[city] || data_country.mapCityName[_city];
            if (data_city) return data_city;
        }

        if (is_full_match) return;

        var provs = getProvs(ctid);
        for (var i = 0, len = provs.length; i < len; i++) {
            var data_prov = provs[i];

            data_city = findInList(getCitys(data_prov.id, data_prov.ctid), _city);
            if (data_city) return data_city;
        }
    }

    /**
     * 查找地区对象
     * @param {ICity} data_city 城市对象
     * @param {string} area 地区名称
     * @param {IProv} data_prov 省份对象，城市对象不存在时从省份对象中查找
     * @param {string} ctid 国家id,默认为0
     * @returns {IArea}
     */
    function findArea(data_city, area, data_prov, ctid) {
        if (!area) return;

        var _area = formatArea(area);

        if (data_city) return data_city.mapAreaName[area] || data_city.mapAreaName[_area] || findInList(getAreas(data_city.pid, data_city.id, data_city.ctid), _area);
        if (data_prov) return data_prov.mapAreaName[area] || data_prov.mapAreaName[_area];

        var data_country = getCountry(ctid);
        if (data_country) return data_country.mapAreaName[area] || data_country.mapAreaName[_area];
    }

    /**
     * 查找省市数据 => {type,code,ctid,pid,cid,country,prov,city}
     * @param {string} prov 省份
     * @param {string} city 城市
     * @param {string} area 地区
     * @param {string} ctid 国家id,默认为0
     * @returns {FormatData}
     */
    function findData(prov, city, area, ctid) {
        var data_prov = prov ? findProv(prov, false, ctid) : undefined, data_city, data_area;
        if (data_prov) {
            if (city) {
                data_city = findCity(city, data_prov, false, ctid);
                if (data_city) {
                    if (area) data_area = findArea(data_city, area, data_prov, ctid);
                } else {
                    data_area = findArea(undefined, city, data_prov, ctid);
                }
            }
        } else {
            if (city) data_city = findCity(city, data_prov, false, ctid);
            if (area) data_area = findArea(data_city, area, undefined, ctid);
        }

        return formatData(data_area || data_city || data_prov);
    }

    /**
     * 查找省市区对象 => {type,code,ctid,pid,cid,aid,country,prov,city,area}
     * @param {string} addr 省市地址 eg:湖北武汉 联通|湖北省武汉市|湖北武汉|湖北|武汉|武汉武汉|武汉洪山
     * @param {boolean} is_full_match 是否匹配完整名称，为false时将在所有省份、城市列表中查找，默认为false
     * @param {string} ctid 国家id,默认为0
     * @returns {FormatData}
     */
    function find(addr, is_full_match, ctid) {
        var gs = (addr + '').split(' '),
            geo = gs[0] || '';

        if (!geo) return {};

        geo = geo + '';

        var result = getDataByCode(geo);
        if (result) return formatData(result);

        var i = geo.indexOf('省'), prov, city, area;
        if (i > 0) {
            prov = geo.slice(0, i);
            city = geo.slice(i + 1);
        } else {
            var data_prov = findProv(geo, true, ctid);
            if (data_prov) return formatData(data_prov);

            city = geo;
        }

        if (city) {
            var j = city.indexOf('市');
            if (j == -1) j = city.indexOf('州');
            if (j == -1) j = city.indexOf('盟');
            if (j == -1) j = city.indexOf('地区');

            if (j != -1) {
                area = city.slice(j + 1);
                if (area && area.length > 1) city = city.slice(0, j);
            } else {
                if (!prov) city = undefined;
            }
        }

        if (prov || city) return findData(prov, city, area, ctid);

        var data_prov = findProv(geo, is_full_match, ctid);
        if (data_prov) {
            i = geo.indexOf(data_prov._name);
            if (i != -1) geo = geo.slice(i + data_prov._name.length);
        }

        var data_city = findCity(geo, data_prov, is_full_match, ctid), data_area;
        if (data_city) {
            i = geo.indexOf(data_city._name);
            area = i != -1 ? geo.slice(i + data_city._name.length) : undefined;

            if (area) data_area = findArea(data_city, area, undefined, ctid);
        } else {
            data_area = findArea(undefined, geo, data_prov, ctid);
        }

        return formatData(data_area || data_city || data_prov);
    }

    var DC = {
        Types: Types,

        getSubs: getSubs,
        getCountries: getCountries,
        getProvs: getProvs,
        getCitys: getCitys,
        getAreas: getAreas,

        getCountry: getCountry,
        getProv: getProv,
        getCity: getCity,
        getArea: getArea,
        getAuto: getAuto,

        getCityOrArea: getCityOrArea,
        getCode: getCode,

        formatProv: formatProv,
        formatCity: formatCity,
        formatArea: formatArea,

        formatData: formatData,

        findProv: findProv,
        findCity: findCity,
        findData: findData,
        find: find
    };

    if (typeof module === 'object' && typeof module.exports === 'object') module.exports = DC;

    if (typeof window !== 'undefined') {
        window.DC2 = DC;
        if (!window.DC) window.DC = DC;
    }
})();